package net.realsee.common.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import lombok.extern.slf4j.Slf4j;
import net.realsee.common.enums.wcs.intf.InterfaceAuthTypeEnum;
import net.realsee.common.enums.wcs.intf.InterfaceCodeEnum;
import net.realsee.common.exception.ServiceException;
import net.realsee.domain.wcs.intf.WcsIntf;
import net.realsee.domain.wcs.intf.WcsIntfOutlog;
import net.realsee.domain.wcs.intf.bo.InterfaceRequest;
import net.realsee.domain.wcs.intf.bo.InterfaceResponse;
import net.realsee.framework.core.domain.R;
import net.realsee.service.wcs.intf.IWcsIntfInlogService;
import net.realsee.service.wcs.intf.IWcsIntfOutlogService;
import net.realsee.service.wcs.intf.IWcsIntfService;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 接口交互工具类
 *
 * @author 孙正华
 * @since 2023/7/27 13:43
 */
@Slf4j
public class InterfaceUtils {

    public static HttpResponse callByHttp(InterfaceCodeEnum interfaceCodeEnum) {
        return callByHttp(InterfaceRequest.builder().interfaceCode(interfaceCodeEnum).build());
    }

    public static HttpResponse callByHttp(InterfaceCodeEnum interfaceCodeEnum, String bodyStr) {
        return callByHttp(InterfaceRequest.builder().interfaceCode(interfaceCodeEnum).bodyStr(bodyStr).build());
    }

    /**
     * 通过HTTP调用第三方接口
     */
    public static HttpResponse callByHttp(InterfaceRequest interfaceRequest) {
        // 1.校验接口参数
        validInterfaceRequest(interfaceRequest);
        // 2.构建HttpRequest对象
        HttpRequest httpRequest = getIntfService().getHttpRequest(interfaceRequest);
        // 3.获取重试次数
        Long retryCount = getIntfService().getRetryCount(interfaceRequest);
        // 4.调用
        HttpResponse httpResponse = null;
        for (long i = 1L; i <= retryCount && (ObjectUtil.isNull(httpResponse) || !httpResponse.isOk()); i++) {
            // 4.1记录日志
            WcsIntfOutlog outlog = getIntfOutlogService().insertByRequest(interfaceRequest);
            // 4.2接口调用
            try {
                httpResponse = httpRequest.execute(interfaceRequest.isAsync());
            } catch (Exception e) {
                log.error("[接口调用失败][url={}][head={}][param={}][body={}]", httpRequest.getUrl(),
                    JSONUtil.toJsonStr(interfaceRequest.getHeaderObj()),
                    JSONUtil.toJsonStr(interfaceRequest.getParamObj()),
                    interfaceRequest.getBodyStr());
            }
            // 4.3更新日志
            getIntfOutlogService().updateByResponse(interfaceRequest, httpResponse, outlog, i);
        }
        return httpResponse;
    }

    /**
     * 数据校验
     */
    private static void validInterfaceRequest(InterfaceRequest request) {
        if (ObjectUtil.isNull(request)) {
            throw new ServiceException("接口请求对象错误");
        }
        if (StrUtil.isBlank(request.getTaskId())) {
            //throw new ServiceException("任务ID/任务码不可为空");
        }
    }

    /**
     * 通过HTTP被第三方调用
     */
    public static void calledByHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain, WcsIntf intf) throws ServletException, IOException {
        // 1.记录日志
        getIntfInlogService().insertByRequest(request, intf);
        try {
            // 2.校验
            if (validHttpRequest(request, response, intf)) {
                // 校验通过，则直接放行
                chain.doFilter(request, response);
            }
        } finally {
            // 3.删除线程中日志记录ID变量
            if (intf.isRecord()) {
                getIntfInlogService().removeLogIdThreadLocal();
            }
        }
    }

    private static boolean validHttpRequest(HttpServletRequest request, HttpServletResponse response, WcsIntf intf) {
        // 1.校验IP是否为黑名单 TODO
        boolean whiteIp = true;
        if (!whiteIp) {
            // 校验不通过，将字符串渲染到客户端
            ServletUtils.renderString(response, HttpStatus.HTTP_UNAUTHORIZED, JSONUtil.toJsonStr(intf));
            return false;
        }
        // 2.校验是否认证通过
        if (!validAuth(request, intf)) {
            // 校验不通过，将字符串渲染到客户端
            ServletUtils.renderString(response, HttpStatus.HTTP_UNAUTHORIZED, JSONUtil.toJsonStr(R.fail(401, "身份认证失败")));
            return false;
        }
        // 3.校验接口是否存在 TODO
        boolean found = true;
        if (!found) {
            // 校验不通过，将字符串渲染到客户端
            ServletUtils.renderString(response, HttpStatus.HTTP_NOT_FOUND, JSONUtil.toJsonStr(intf));
            return false;
        }
        // 4.校验.....

        return true;
    }

    private static boolean validAuth(HttpServletRequest request, WcsIntf intf) {
        if (StrUtil.equalsIgnoreCase(intf.getAuthType(), InterfaceAuthTypeEnum.BEARER.name())) {

        } else if (StrUtil.equalsIgnoreCase(intf.getAuthType(), InterfaceAuthTypeEnum.OAUTH.name())) {

        } else if (StrUtil.equalsIgnoreCase(intf.getAuthType(), InterfaceAuthTypeEnum.BASIC.name())) {

        } else if (StrUtil.equalsIgnoreCase(intf.getAuthType(), InterfaceAuthTypeEnum.DIGEST.name())) {

        } else if (StrUtil.equalsIgnoreCase(intf.getAuthType(), InterfaceAuthTypeEnum.TOKEN.name())) {
            return StrUtil.equalsIgnoreCase(intf.getAuthData(), ServletUtils.getHeader(request, "token", Constants.UTF_8));
        }
        return true;
    }

    /**
     * 更新出站接口日志
     */
    public static void updateOutlogByResponse(InterfaceResponse response) {
        getIntfOutlogService().updateByResponse(response);
    }

    /**
     * 更新入站接口日志
     */
    public static void updateInlogByResponse(InterfaceResponse response) {
        getIntfInlogService().updateByResponse(response);
    }

    private static IWcsIntfService getIntfService() {
        return SpringUtil.getBean("wcsIntfServiceImpl");
    }

    private static IWcsIntfOutlogService getIntfOutlogService() {
        return SpringUtil.getBean("wcsIntfOutlogServiceImpl");
    }

    private static IWcsIntfInlogService getIntfInlogService() {
        return SpringUtil.getBean("wcsIntfInlogServiceImpl");
    }

}
